package net.peelmeagrape.hibernate;

import net.peelmeagrape.hibernate.xmlgen.*;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;

/**
 * An ordinary association to another persistent class is declared using a many-to-one element.
 * The relational model is a many-to-one association: a foreign key in one table is referencing
 * the primary key column(s) of the target table.
 * <pre>
    class Child {
        &#64;{@link ManyToOne} Parent parent;
    }
 </pre>
 * In this example, a foreign key on the 'child' table references the primary key of the 'parent' table.
 * <br/>
 * <br/>
 * Generates a hibernate &lt;many-to-one&gt; mapping element.
 * @see <a href="{@docRoot}/reference/en/html/mapping.html#mapping-declaration-manytoone">Hibernate Documentation for &lt;many-to-one&gt;</a>
 */
@Target({METHOD,FIELD}) @Retention(RUNTIME)
@XmlElement(name="many-to-one")
@AddPropertyNameAsAttribute(name="name")
public @interface ManyToOne
{
    /**
     * The strategy Hibernate should use for accessing the property value. This may be one of
     * 'property', 'field', 'embedded', or the name of a subclass of {@link org.hibernate.property.PropertyAccessor}.
     */
    @XmlAttribute String access() default "property";

    /**
     * (optional - defaults to the property type determined by reflection): The name of the associated class.
     */
    @XmlAttribute("class") Class[] targetClass() default {};

    /**
     * The entity name of the associated class.
     */
    @XmlAttribute("entity-name") String entityName() default "";

    /**
     * The name of the foreign key column. This may also be specified by nested {@link #columnMapping()} element(s).
     */
    @XmlAttribute String column() default "";

    /**
     * Enable the DDL generation of a nullability constraint for the foreign key columns.
     */
    @XmlAttribute("not-null") boolean notNull() default false;

    /**
     * Enable the DDL generation of a unique constraint for the foreign-key column.
     * Also, allow this to be the target of a property-ref. This makes the association
     * multiplicity effectively one to one.
     */
    @XmlAttribute boolean unique() default false;
    @XmlAttribute("unique-key") String uniqueKey() default "";

    /**
     * Specifies which operations should be cascaded from the parent object to the associated object.
     * Setting a value of the <code>cascade</code> attribute to any meaningful
     * value other than <code>none</code> will propagate certain operations to the
     * associated object. The meaningful values are the names of Hibernate's basic
     * operations, <code>persist, merge, delete, save-update, evict, replicate, lock,
     * refresh</code>, as well as the special values <code>delete-orphan</code>
     * and <code>all</code> and comma-separated combinations of operation
     * names, for example, <code>cascade="persist,merge,evict"</code> or
     * <code>cascade="all,delete-orphan"</code>.
     * See <a href="{@docRoot}/reference/en/html/objectstate.html#objectstate-transitive">Transitive persistence</a>
     * for a full explanation.
     */
    @XmlAttribute String cascade() default "";
    @XmlAttribute OuterJoinSetting outerJoin() default OuterJoinSetting.AUTO;

    /**
     * Chooses between outer-join fetching or sequential select fetching.
     */
    @XmlAttribute FetchSetting fetch() default FetchSetting.SELECT;
    /**
     * Specifies that the mapped columns should be included in SQL UPDATE statement.
     * Setting both {@link #update()} and {@link #insert()} to false
     * allows a pure "derived" property whose value is initialized from some
     * other property that maps to the same colum(s) or by a trigger or other application.
     */
    @XmlAttribute boolean update() default true;

    /**
     * Specifies that the mapped columns should be included in SQL INSERT statement.
     * Setting both {@link #update()} and {@link #insert()} to false
     * allows a pure "derived" property whose value is initialized from some
     * other property that maps to the same colum(s) or by a trigger or other application.
     */
    @XmlAttribute boolean insert() default true;

    /**
     * Specifies that updates to this property do or do not require acquisition of the optimistic lock.
     * In other words, dertermines if a version increment should occur when this property is dirty.
     */
    @XmlAttribute("optimistic-lock") boolean optimisticLock() default true;
    @XmlAttribute("foreign-key") String foreignKey() default "";

    /**
     * The name of a property of the associated class that is joined to this foreign key.
     * If not specified, the primary key of the associated class is used. This may be either the
     * name of a {@link Property} or a {@link Properties}.
     * <br/>
     * <br/>
     * The property-ref attribute should only be used for mapping legacy data where a foreign key
     * refers to a unique key of the associated table other than the primary key. This is an ugly
     * relational model. For example, suppose the Product class had a unique serial number, that
     * is not the primary key. (The unique attribute controls Hibernate's DDL generation with the
     * SchemaExport tool.)
     * <pre>&#64;{@link Property}(unique=true) String serialNumber;</pre>
     * Then the mapping for OrderItem might use:
     * <pre>&#64;{@link ManyToOne}(propertyRef="serialNumber", column="PRODUCT_SERIAL_NO") Product product;</pre>
     * This is certainly not encouraged, however.
     * If the referenced unique key comprises multiple properties of the associated entity, you should map the
     * referenced properties inside a named {@link Properties} element.
     */
    @XmlAttribute("property-ref") String propertyRef() default "";
    @XmlAttribute String formula() default "";
    @XmlAttribute String index() default "";

    /**
     * By default, single point associations are proxied. lazy="true"  specifies that the property should be
     * fetched lazily when the instance variable is first accessed (requires build-time bytecode instrumentation).
     * lazy="false" specifies that the association will always be eagerly fetched.
     */
    @XmlAttribute LazySetting[] lazy() default {};

    /**
     * Specifies how foreign keys that reference missing rows will be handled:
     * ignore will treat a missing row as a null association.
     */
    @XmlAttribute("not-found") NotFoundSetting notFound() default NotFoundSetting.EXCEPTION;
    @XmlAttribute String node() default "";
    @XmlAttribute("embed-xml") boolean embedXml() default true;

    @ChildElement(0) Meta[] meta() default {};
    @ChildElement(1) Column[] columnMapping() default {};

    @FindParentElement(name="natural-id") boolean naturalId() default false;
    @FindParentElement(
        name="join",
        idAttributeName = "table",
        reorderNewParent = "many-to-one|sql-insert?,sql-update?,sql-delete?"
    )
        String[] join() default {};

    /**
     * Specify that this property is part of a properties grouping. See {@link Properties}.
     */
    @FindParentElement(
        name="properties",
        idAttributeName = "name"
    )
        String[] properties() default {};
}
